﻿using GomokuCommon;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;

namespace GomokuServer {
    // 服务器
    class Server: Socket {
        private readonly Dictionary<string, Client> mOnlineUsers;   // 在线用户列表

        /**
         * 构造方法。
         */
        public Server() :
            base(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) {
            mOnlineUsers = new Dictionary<string, Client>();
        }

        /**
         * 析构方法。
         */
        ~Server() {
            // 关闭套接字
            base.Close();

            // 清空在线用户列表
            mOnlineUsers.Clear();
        }

        /**
         * 处理寻找其它用户请求。
         * @param request   请求
         * @param client    客户端
         */
        private void HandleFindUsers(FindUsersRequest request, Client client) {
            // 创建结果
            List<string> rivalList = new List<string>(mOnlineUsers.Count);
            foreach (var item in mOnlineUsers) {
                // 跳过自己的名字
                if (request.mUsername == item.Key) {
                    continue;
                }
                rivalList.Add(item.Key);
            }
            FindUsersResult result = new FindUsersResult(rivalList);

            // 发送结果
            this.SendData(client, result);
        }

        /**
         * 处理登录请求。
         * @param request   请求
         * @param client    客户端
         */
        private void HandleLogon(LogonRequest request, Client client) {
            LogonResult result;

            // 如果已登录，则直接返回
            try {
                mOnlineUsers.Add(request.mUsername, client);
            } catch (Exception) {
                result = new LogonResult("");
                this.SendData(client, result);
                return;
            }

            // 创建结果
            result = new LogonResult(request.mUsername);

            // 发送结果
            this.SendData(client, result);
        }

        /**
         * 处理协议。
         * @param pro   协议
         */
        private void HandleProtocol(GomokuProtocol pro) {
            // 如果目标用户不在线则返回
            Client rivalUser;
            bool isOnline = mOnlineUsers.TryGetValue(pro.mUsername, out rivalUser);
            if (!isOnline) {
                return;
            }

            // 发送给目标用户
            this.SendData(rivalUser, pro);
        }

        /**
         * 处理退出游戏请求。
         * @param request   请求
         * @param client    客户端
         */
        private void HandleQuitGame(QuitGame request, Client client) {
            // 广播该用户的请求
            foreach (Client rivalUser in mOnlineUsers.Values) {
                try {
                    this.SendData(rivalUser, request);
                } catch (Exception) {
                }
            }

            // 将该用户从在线列表移除
            mOnlineUsers.Remove(request.mUsername);

            // 关闭客户端
            client.Close();
        }

        /**
         * 客户端连接服务器事件的响应方法。
         * @param result    事件相关信息
         */
        private void OnAccept(IAsyncResult result) {
            try {
                Socket listener = (Socket)result.AsyncState;
                Socket clientSocket = listener.EndAccept(result);
                base.BeginAccept(new AsyncCallback(OnAccept), this);
                SocketInformation si = clientSocket.DuplicateAndClose(Process.GetCurrentProcess().Id);
                Client client = new Client(si, this);
            } catch (Exception) {
            }
        }

        /**
         * 处理客户端发送来的数据。
         * @param pro       协议
         * @param client    客户端
         */
        public void OnGetMessageFromClient(GomokuProtocol pro, Client client) {
            lock (this) {
                switch (pro.mType) {
                    case GomokuProtocol.Type.FIND_USERS:
                        this.HandleFindUsers((FindUsersRequest)pro, client);
                        break;
                    case GomokuProtocol.Type.LOGON:
                        this.HandleLogon((LogonRequest)pro, client);
                        break;
                    case GomokuProtocol.Type.QUIT_GAME:
                        this.HandleQuitGame((QuitGame)pro, client);
                        break;
                    default:
                        this.HandleProtocol(pro);
                        break;
                }
            }
        }

        /**
         * 发送数据。
         * @param client    客户端
         * @param pro       协议
         */
        private void SendData(Client client, GomokuProtocol pro) {
            // 将协议转换成数据
            byte[] data = GomokuProtocol.ToBytes(pro);

            // 发送数据长度
            client.Send(BitConverter.GetBytes(data.Length));

            // 发送数据
            client.Send(data);
        }
        
        /**
         * 开启服务。
         * @return 成功则返回 true，否则返回 false
         */
        public bool Start() {
            try {
                // 绑定 IP 地址和端口号
                IPEndPoint serverAddr = new IPEndPoint(IPAddress.Parse(GomokuProtocol.SERVER_IP), GomokuProtocol.SERVER_PORT);
                base.Bind(serverAddr);

                // 侦听
                base.Listen(int.MaxValue);

                // 开始接收数据
                base.BeginAccept(new AsyncCallback(OnAccept), this);
            } catch (Exception) {
                return false;
            }

            return true;
        }
    }
}
